﻿using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Ewk.Math.Numerics.PositionalNumeralSystems
{
    /// <summary>
    /// Represents the factorial number system.
    /// </summary>
    public class Factorial : IPositionalNumber
    {
        private IList<int> _numbers;

        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        /// <param name="decimalValue">The decimal representation of the number.</param>
        public Factorial(long decimalValue)
        {
            DecimalValue = decimalValue;
        }

        #region Implementation of IPositionalNumber

        public IList<int> Numbers
        {
            get { return _numbers ?? (_numbers = ComputeNumbers(DecimalValue)); }
        }

        public long DecimalValue { get; private set; }

        #endregion

        public override string ToString()
        {
            return Numbers
                .Select(i =>
                        i.ToString(CultureInfo.InvariantCulture))
                .Reverse()
                .Aggregate((a, b) => a + b);
        }

        #region Overloaded operators

        /// <summary>
        /// Implicitly creates a <see cref="Complex"/> from a <see cref="int"/>.
        /// </summary>
        public static implicit operator Factorial(Decimal value)
        {
            return new Factorial(value.DecimalValue);
        }

        #endregion

        private static IList<int> ComputeNumbers(long decimalValue)
        {
            var list = new List<int>();

            while (decimalValue > 0)
            {
                var i = 1;
                long factorial = 1;
                while (factorial*i-1 < decimalValue)
                {
                    factorial *= i;
                    i++;
                }

                while (i-1>list.Count)
                {
                    list.Add(0);
                }

                var number = System.Math.Floor((double) decimalValue/factorial);
                list[i-2] = (int)number;

                decimalValue -= factorial*(long) number;
            }

            return list;
        }
    }
}